Ga naar hoofdinhoud

REST API en backend

Verschil backend / API​

  • Wordt veel door elkaar gebruikt in deze cursus
  • Niet fout vanuit frontend perspectief
  • Tekening om het verschil duidelijk te maken

Frontend / Backend vs Clientside / Serverside​

  • Eigenlijk is clientside / serverside gepastere benaming voor wat er echt gebeurd
  • Client = het aparaat dat je gebruikt om de web applicatie te bezoeken
  • Server = computer in de "the cloud" waar de data leeft

Aan beide kanten kan code runnen Deze twee kanten communiceren met elkaar door middel van een afgesproken contract = API

Verschil in taalgebruik​

Client​

  • beperktere keuze = JS, HTML en CSS zijn in wezen de enige talen die het begrijpt
  • SVG, JSON, bepaalde videoformaten kan ook maar het runnen en weergeven van webpaginas door die 3
  • Nuance verschil door WASM
  • Er zijn ook verschillende browser apis die door javascript gebruikt kunnen worden maar er is nog steeds JS nodig

WASM (small sidetrack)​

wasm home page WebAssembly is een binair instructieformaat dat werkt als een soort virtuele machine in de browser. In simpele termen:

  • Het is een manier om code die in andere talen (zoals C, C++, Rust) is geschreven snel in browsers te laten draaien
  • Het werkt als een soort "vertaler" die deze code omzet naar iets wat browsers begrijpen
  • Het hoofddoel is betere prestaties dan JavaScript voor rekenintensieve taken
  • Het werkt samen met JavaScript, niet als vervanging ervan

Server​

  • Een wijde varieteit aan talen:
  1. Node.js (JavaScript) - Zeer populair vanwege de mogelijkheid om dezelfde taal aan zowel client- als server-kant te gebruiken.
const axios = require('axios');

async function getUsers() {
try {
const response = await axios.get('https://api.example.com/users');
console.log('Gebruikers:', response.data);
return response.data;
} catch (error) {
console.error('Fout bij het ophalen van gebruikers:', error.message);
throw error;
}
}

getUsers();
  1. Python - Bekend om zijn leesbaarheid en veelzijdigheid, vaak gebruikt met frameworks zoals Django en Flask.
import requests

def get_users():
try:
response = requests.get('https://api.example.com/users')
response.raise_for_status() # Controleert of het verzoek succesvol was
users = response.json()
print(f"Gebruikers: {users}")
return users
except requests.exceptions.RequestException as e:
print(f"Fout bij het ophalen van gebruikers: {e}")
raise

if __name__ == "__main__":
get_users()
  1. PHP - Drijft ongeveer 78% van alle websites aan, inclusief WordPress. Frameworks zoals Laravel en Symfony maken het krachtiger.
<?php
function getUsers() {
$url = 'https://api.example.com/users';

$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($curl);

if($response === false) {
$error = curl_error($curl);
curl_close($curl);
echo "Fout bij het ophalen van gebruikers: " . $error;
return null;
}

curl_close($curl);
$users = json_decode($response, true);
echo "Gebruikers: " . print_r($users, true);
return $users;
}

getUsers();
?>
  1. Java - Zeer robuust en schaalbaar, populair voor enterprise-toepassingen met frameworks zoals Spring.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;

public class RestApiExample {
public static void main(String[] args) {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.GET()
.build();

HttpResponse<String> response = client.send(request, BodyHandlers.ofString());

System.out.println("Statuscode: " + response.statusCode());
System.out.println("Gebruikers: " + response.body());
} catch (Exception e) {
System.out.println("Fout bij het ophalen van gebruikers: " + e.getMessage());
}
}
}
  1. C# (.NET) - Microsoft's taal die vaak wordt gebruikt voor Windows-servers en steeds meer cross-platform met .NET Core.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.Json;

class Program
{
static async Task Main(string[] args)
{
await GetUsers();
}

static async Task GetUsers()
{
try
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync("https://api.example.com/users");
response.EnsureSuccessStatusCode();

string responseBody = await response.Content.ReadAsStringAsync();
var users = JsonSerializer.Deserialize<object>(responseBody);

Console.WriteLine($"Gebruikers: {responseBody}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Fout bij het ophalen van gebruikers: {ex.Message}");
}
}
}
  1. Ruby - Bekend om Ruby on Rails, een framework dat productiviteit en snelle ontwikkeling bevordert.
require 'net/http'
require 'json'

def get_users
begin
uri = URI('https://api.example.com/users')
response = Net::HTTP.get_response(uri)

if response.is_a?(Net::HTTPSuccess)
users = JSON.parse(response.body)
puts "Gebruikers: #{users}"
return users
else
puts "Fout bij het ophalen van gebruikers: #{response.code} #{response.message}"
return nil
end
rescue => e
puts "Fout bij het ophalen van gebruikers: #{e.message}"
raise
end
end

get_users
  1. Go (Golang) - Ontwikkeld door Google, gewaardeerd om zijn snelheid en eenvoud, vooral voor microservices.
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)

func main() {
getUsers()
}

func getUsers() {
resp, err := http.Get("https://api.example.com/users")
if err != nil {
fmt.Println("Fout bij het ophalen van gebruikers:", err)
return
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
fmt.Println("Fout bij het ophalen van gebruikers. Statuscode:", resp.StatusCode)
return
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Fout bij het lezen van de response:", err)
return
}

var users interface{}
err = json.Unmarshal(body, &users)
if err != nil {
fmt.Println("Fout bij het verwerken van JSON:", err)
return
}

fmt.Println("Gebruikers:", users)
}
  1. Rust - Groeiende populariteit vanwege veiligheid en prestaties, vooral voor systemen waar betrouwbaarheid cruciaal is.
use reqwest;
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
match get_users().await {
Ok(users) => println!("Gebruikers: {:?}", users),
Err(e) => println!("Fout bij het ophalen van gebruikers: {}", e),
}
Ok(())
}

async fn get_users() -> Result<Value, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let response = client.get("https://api.example.com/users")
.send()
.await?;

if response.status().is_success() {
let users: Value = response.json().await?;
Ok(users)
} else {
Err(format!("API fout: {}", response.status()).into())
}
}

De keuze hangt vaak af van factoren zoals het type project, beschikbare expertise in het team, prestatie-eisen en het ecosysteem van frameworks en bibliotheken.

Om REST API's uit te leggen aan beginners met je Node.js/Express applicatie als voorbeeld, zou ik deze aanpak aanbevelen:

Basisconcepten​

Begin met een eenvoudige uitleg van wat een API is:

  • "Een API (Application Programming Interface) is zoals een ober in een restaurant - hij neemt je bestelling aan, geeft deze door aan de keuken, en brengt je eten terug"
  • "Een REST API gebruikt het internet om gegevens tussen verschillende systemen uit te wisselen via standaard HTTP-verzoeken (GET, POST, PUT, DELETE)"

Klein schema overzicht​

Teken een eenvoudig schema dat laat zien hoe de verschillende onderdelen samenhangen:

[Client] <--HTTP verzoek--> [Express Server]
|
[Controllers --> Services --> Repositories]
|
[Database]

Project structuur​

  1. Controllers (de verkeerleiders):

    • "Controllers ontvangen verzoeken en sturen antwoorden"
    • Voorbeeld: groupController.js ontvangt een verzoek om iemand uit te nodigen voor een groep
  2. Services (de bedrijfslogica):

    • "Services bevatten de bedrijfslogica - de regels en bewerkingen"
    • Voorbeeld: groupService.js controleert of de gebruiker iemand kan uitnodigen voor die groep
  3. Repositories (databeheer):

    • "Repositories praten met de database"
    • Voorbeeld: groupResository.js slaat de uitnodiging op

Show and tell​

Stukje uit de code -> Tonen van het uitsturen van een invitation voor een group

HTTP-methoden uit / CRUD acties​

  • GET: Gegevens ophalen (Read)
  • POST: Nieuwe gegevens maken (Create)
  • PUT/PATCH: Gegevens bijwerken (Update)
  • DELETE: Gegevens verwijderen (Delete)

Fundamentele REST-concepten (REpresentational State Transfer)​

RESTful principes**: Stateless, resource-georiënteerd, uniforme interface​

Stateless (Toestandloos)​

"Stateless" betekent dat de server geen geheugen heeft van vorige verzoeken.

  • Eenvoudige uitleg: Elke keer dat je de server iets vraagt, moet je je "volledig voorstellen" - de server onthoudt niet wie je bent van een vorig verzoek.
  • Voorbeeld: Als je inlogt en daarna een artikel wilt bekijken, moet je bij dat tweede verzoek opnieuw bewijzen wie je bent (meestal met een token of cookie).
  • Voordeel: Servers kunnen makkelijker schalen omdat ze geen gebruikersinformatie hoeven te onthouden.

Resource-georiënteerd​

REST draait om "resources" (bronnen) - dingen zoals gebruikers, artikelen, producten.

  • Eenvoudige uitleg: Je API is georganiseerd rond "dingen" (resources), niet rond "acties".
  • Voorbeeld:
    • Goed: /users/123 (de resource is een gebruiker)
    • Minder REST-achtig: /getUserData?id=123 (focus op de actie)
  • Voordeel: Logische structuur die intuïtief te begrijpen is.

Uniforme interface​

Alle resources worden op dezelfde manier benaderd, volgens vaste regels.

  • Eenvoudige uitleg: Het is als een standaard bestelformulier - iedereen gebruikt dezelfde methodes om verschillende dingen te doen.
  • Voorbeeld:
    • GET voor ophalen
    • POST voor aanmaken
    • PUT/PATCH voor bijwerken
    • DELETE voor verwijderen
  • Voordeel: Zodra iemand weet hoe één deel van je API werkt, begrijpen ze grotendeels hoe de rest werkt.

Andere​

  1. HTTP-statuscodes: 200 (OK), 201 (Created), 400 (Bad Request), 404 (Not Found), 500 (Server Error)
  2. URL-structuur: Hoe je endpoints logisch opbouwt (/resources/{id})
  3. Request parameters: Path, query, body, headers

Middleware en JWT​

Wat is een JWT?​

Een JWT is een lange tekstreeks die er ongeveer zo uitziet:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbiBKYW5zZW4iLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Hoe werkt het?​

  1. Inloggen: Wanneer je inlogt op een website, controleert de server je wachtwoord.

  2. Token krijgen: Als alles klopt, geeft de server je een JWT token (dat digitale paspoort).

  3. Token bewaren: Je browser bewaart dit token (meestal in local storage of een cookie).

  4. Bij elk verzoek: Wanneer je iets wilt doen op de website, stuurt je browser het token mee: "Hallo, ik ben het weer, hier is mijn paspoort."

  5. Verificatie: De server bekijkt het token en kan zien:

    • Wie je bent
    • Welke rechten je hebt
    • Of het token nog geldig is

Onderdelen van een JWT​

Een JWT bestaat uit drie delen, gescheiden door punten:

  1. Header: Informatie over wat voor soort token het is
  2. Payload: De gegevens zelf (zoals je gebruikers-ID, naam, rechten)
  3. Signature: Een soort digitale handtekening die bewijst dat het token echt is

Waarom worden JWT's gebruikt?​

  • Stateless: De server hoeft geen sessies bij te houden
  • Veilig: Het token kan niet worden vervalst (dankzij de handtekening)

Data & Validatie​

  1. Data validatie: Waarom validatie cruciaal is en hoe implementeren
  2. Schema validatie: Tools zoals Joi, Yup of Zod
  3. Sanitization: Hoe je data veilig maakt voordat je het gebruikt

Database interactie​

  1. ORM/ODM concepten: Hoe je database-operaties abstraheert
  2. Transactions: Waarom ze belangrijk zijn voor data-integriteit
  3. Migrations: Hoe je database-schema's veilig aanpast

API Design​

  1. Versioning: Waarom je API's moet versionen (v1, v2, etc.)
  2. Pagination: Hoe je grote datasets efficiënt serveert
  3. Filtering, Sorting & Searching: Implementatie-strategieën

Error Handling​

  1. Consistente error responses: Hoe maak je uniforme error-berichten
  2. Logging: Het belang van goede logging voor debugging
  3. Try-catch patronen: Hoe vang je fouten netjes af

Performance & Schaling​

  1. Caching: Implementatie en strategieën
  2. Rate limiting: Beschermen tegen overbelasting

Documentation​

  1. API documentatie: OpenAPI/Swagger

Deployment & DevOps​

  1. Environment variables: Configuratie en geheimen beheren